package ch.msoftch.gui.utils;

import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.*;
import java.io.*;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.*;

/**
 * Class building a property class.
 * 
 * @author hautle
 */
public class PropertyClassBuilder extends ClassBuilder {
	/** The name of the resource bundle. */
	private static final String RESOURCE_BUNDLE = ResourceBundle.class.getSimpleName();

	/** The name of the getter method. */
	private static final String GET_METHOD = "getValue";

	/** The properties file. */
	private final IFile properties;

	/** The name of the properties file (without suffix). */
	private final String propName;

	/**
	 * Default constructor.
	 * 
	 * @param properties The input file
	 * @throws CoreException
	 */
	private PropertyClassBuilder(IFile properties) throws CoreException {
		this.properties = properties;
		final int len = properties.getFileExtension().length();
		final String name = properties.getName();
		propName = name.substring(0, name.length() - len - 1);
	}

	/**
	 * Returns the package in which contains the properties file
	 * 
	 * @return The package
	 * @throws CoreException
	 */
	private IPackageFragment getPackage() throws CoreException {
		final IJavaProject jp = (IJavaProject) properties.getProject().getNature(JavaCore.NATURE_ID);
		return jp.findPackageFragment(properties.getParent().getFullPath());
	}

	/**
	 * Returns the class name for the class to create.
	 * 
	 * @return The class name
	 */
	private String getClassName() {
		final StringBuilder str = new StringBuilder();
		str.append(propName);
		str.setCharAt(0, Character.toUpperCase(propName.charAt(0)));
		return str.toString();
	}

	/**
	 * Initializes the class.
	 * 
	 * @throws CoreException
	 */
	private void init() throws CoreException {
		final String name = getClassName();
		final IPackageFragment pkg = getPackage();
		final String prop = pkg.getElementName() + "." + propName;
		final ICompilationUnit unit = pkg.createCompilationUnit(name + ".java", "", true, null);
		setUp(unit);
		createMainType(PUBLIC_KEYWORD, pkg.getElementName(), getClassName());
		addImport("java.util.*");
		final MethodInvocation mv = createInvocation(ast.newSimpleName(RESOURCE_BUNDLE), "getBundle", createString(prop));
		addField(createField(RESOURCE_BUNDLE, "BUNDLE", mv, PRIVATE_KEYWORD, STATIC_KEYWORD));
	}

	/**
	 * Creates the get method in the properties class.
	 */
	private void createGetMethod() {
		final Block catchB = createBlock(createReturn(createInfixExpression(createString("!"), InfixExpression.Operator.PLUS, ast.newSimpleName("key"))));
		final ReturnStatement ret = createReturn(createInvocation(ast.newSimpleName("BUNDLE"), "getString", ast.newSimpleName("key")));

		final Block b = createBlock(createTryCatchBlock(createBlock(ret), catchB, "MissingResourceException", "e"));
		final MethodDeclaration m = createMethod(GET_METHOD, "String", b, "String key");
		setModifiers(m.modifiers(), PRIVATE_KEYWORD, STATIC_KEYWORD);
		addMethod(m);
	}

	/**
	 * Parses the properties file and creates the corresponding constant entries.
	 * 
	 * @throws IOException
	 * @throws CoreException
	 */
	private void parse() throws IOException, CoreException {
		BufferedReader in = null;
		try {
			in = new BufferedReader(new InputStreamReader(properties.getContents()));
			for (String s = in.readLine(); s != null; s = in.readLine()) {
				if (s.charAt(0) == '#' || s.length() == 0)
					continue;
				final int delim = s.indexOf('=');
				createEntry(s.substring(0, delim).trim());
			}
		} finally {
			if (in != null)
				in.close();
		}
	}

	/**
	 * Creates a constant for the given property.
	 * 
	 * @param property The property name
	 */
	private void createEntry(String property) {
		final MethodInvocation m = createInvocation(null, GET_METHOD, createString(property));
		addField(createField("String", getConstantName(property), m, PUBLIC_KEYWORD, STATIC_KEYWORD, FINAL_KEYWORD));
	}

	/**
	 * Retruns the name for a constant for the given property.
	 * 
	 * @param property The property name
	 * @return The corresponding constant name
	 */
	private String getConstantName(String property) {
		final StringBuilder str = new StringBuilder(property.length() + 10);
		for (int i = 0, len = property.length(); i < len; i++) {
			final char c = property.charAt(i);
			switch (c) {
				case '-':
				case '.':
					str.append('_');
					break;
				default:
					if (!Character.isJavaIdentifierPart(c))
						continue;
					if (Character.isUpperCase(c))
						str.append('_').append(c);
					else
						str.append(Character.toUpperCase(c));
			}
		}
		return str.toString();
	}

	/**
	 * Creates a properties class for the given properties file.
	 * 
	 * @param properties The properties file
	 * @throws IOException
	 * @throws CoreException
	 */
	public static void create(IFile properties) throws IOException, CoreException {
		final PropertyClassBuilder p = new PropertyClassBuilder(properties);
		p.init();
		p.parse();
		p.createGetMethod();
		p.write();
	}
}
